Categories
TypeScript Best Practices

TypeScript Best Practices — Classes, Types, and Overloads

Spread the love

TypeScript is an easy to learn extension of JavaScript. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust TypeScript code.

In this article, we’ll look at the best practices to following when writing code with TypeScript, including member overloads, class instance variables, and restricting types.

Member Overloads Should be Consecutive

If we have member overloads, then they should be consecutive so that we can spot them easily.

For instance, instead of writing:

declare namespace Foo {

export function foo(s: string): void;

export function foo(n: number): void;

export function bar(): void;

export function foo(sn: string | number): void;

}

We write:

declare namespace Foo {
  export function foo(s: string): void;
  export function foo(n: number): void;
  export function foo(sn: string | number): void;
  export function bar(): void;
}

This also applies to interfaces, classes, type aliases, and exports.

We should write:

interface Foo {
  foo(s: string): void;
  foo(n: number): void;
  foo(sn: string | number): void;
  bar(): void;
}

or:

class Foo {
  foo(s: string): void;
  foo(n: number): void;
  foo(sn: string | number): void {}
  bar(): void {}
}

or:

export function foo(s: string): void;
export function foo(n: number): void;
export function foo(sn: string | number): void;
export function bar(): void;

Use [] or Array<T> for Arrays

To restrict the types of arrays, we can use T[] or Array<T> to restrict the types that an array can hold.

For instance, instead of writing:

const arr = [1, 2, 3];

We write:

const arr: Array<number> = [1, 2, 3];

or:

const arr: number[] = [1, 2, 3];

So that arr can only hold numbers.

If we want our array to be read-only, we can write:

const arr: ReadonlyArray<number> = [1, 2, 3];

No Awaiting Something that’s Not Then-able

If something doesn’t have a then method, then we shouldn’t put await before it.

Usually, await should be used for promises rather than any object that has a then method.

For instance, instead of writing:

const foo = async () => await "value";

We should write:

const foo = async () => {
  await Promise.resolve(1);
};

No Comments With Prefix @ts Should be Used

@ts is used to suppress TypeScript compiler errors.

Therefore, we probably shouldn’t use them since they may lead to errors later.

Instead of writing:

// @ts-nocheck

We don’t write any comment that starts with @ts .

Prevent Specific Types from Being Used

We may want to prevent some types to be used.

For instance, we may want to prevent String from being used instead of string .

We can do that in our tsconfig.json , by writing:

{
  "@typescript-eslint/ban-types": ["error", {
    "types": {
      "String": {
        "message": "Use string instead",
        "fixWith": "string"
      },"{}": {
        "message": "Use object instead",
        "fixWith": "object"
      }
    }
  }]
}

The config above prevents String from being used and if it’s used, the compiler will show the message ‘Use string instead’ and won’t compile.

We did the same thing with the {} type.

Class Literals Should be Exposed in a Consistent Style

We should have a consistent style when exposing class members to the outside.

There are a few styles that we can adopt.

We can use the ‘fields’ style as follows:

class Foo {
  public readonly foo = 1;
  public readonly bar = [1, 2, 3];
  private readonly ['baz'] = 'hello';

  public get qux() {
    return `qux ${foo + 1}`;
  }
}

If we have the ‘fields’ style, then we don’t have getters for our instance variables if they have read-only values.

Alternatively, we can use the ‘getters’ style by writing:

class Foo {
  public readonly foo = 1;
  public readonly bar = [1, 2, 3];
  public static get baz() {
    return 1;
  }

  private get qux() {
    return 'qux';
  }
}

We have getters for any code that isn’t defined as read-only.

We can take our pick, but it’s a good idea to be consistent.

Conclusion

We can restrict types of array entries by specifying the types that we want the array to be.

Also, we can restrict types for the whole project by changing the configuration of our project.

We can also restrict that overloads of the same function be placed together.

Finally, we can stick to one style of declaring class instance variables.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *